import io
import logging

import boto3
from botocore.exceptions import ClientError
from pydantic import AnyUrl
from typing_extensions import TYPE_CHECKING

from src.retry import create_retry
from src.s3.abstract import IS3
from src.settings import RetrySettings, S3Settings

if TYPE_CHECKING:
    from boto3_type_annotations.s3 import Client
logger = logging.getLogger(__name__)


class Boto3S3Uploader(IS3):
    def __init__(self, client: "Client", s3_settings: S3Settings, retry_settings: RetrySettings):
        self._settings = s3_settings
        self._client = client
        self._ensure_bucket(s3_settings.bucket)
        self.upload = create_retry(settings=retry_settings)(self.upload)

    def _ensure_bucket(self, bucket: str) -> None:
        try:
            self._client.head_bucket(Bucket=bucket)
            print("Bucket %s exists", bucket)
        except ClientError as e:
            print("ERROR")
            logger.info("Bucket %s does not exist, creating it", bucket, exc_info=e)
            self._client.create_bucket(Bucket=bucket)

    @staticmethod
    def get_boto_from_settings(s3_settings: S3Settings) -> "Client":
        return boto3.client(
            service_name="s3",
            region_name=s3_settings.region_name,
            endpoint_url=str(s3_settings.url),
            aws_access_key_id=s3_settings.access_key,
            aws_secret_access_key=s3_settings.secret_access_key,
        )

    def upload(
        self,
        name: str,
        data: bytes,
        **kwargs,
    ) -> AnyUrl:  # pylint: disable=method-hidden
        logger.info("Started uploading %s to s3", name)
        writer_file = io.BytesIO()
        writer_file.write(data)
        writer_file.seek(0)
        self._client.upload_fileobj(
            Fileobj=writer_file, Bucket=self._settings.bucket, Key=str(name)
        )
        output_file_link = f"{str(self._settings.url)}/{self._settings.bucket}/{name}"
        print(f"output_file_link: {output_file_link}")
        logger.info("Finished uploading %s to s3", name)

        return AnyUrl(output_file_link)
